
#include "graphics/node.h"
#include "graphics/renderer.h"
#include "graphics/animation.h"
#include "graphics/object.h"
#include "graphics/camera.h"
#include "graphics/model.h"
#include "graphics/graphicsmanager.h"

namespace dw {
	namespace graphics {

Node::Node(Node* parent, const std::string& name /*= ""*/)
:	m_name(name)
,	m_parent(parent)
,	m_object(NULL)
,	m_animation(NULL)
,	m_hidden(false)
,	m_transform(Matrix4f::identity)
{
}

Node::~Node()
{
	for (NodesCollection::iterator iter = m_children.begin(); iter != m_children.end(); ++iter)
		delete *iter;

	delete m_object;
	delete m_animation;
}

Node* Node::getChild(const unsigned i) const
{
	if (i >= m_children.size()) throw std::out_of_range(__FUNCTION__);

	return m_children[i];
}

Node* Node::getChild(const std::string& name)
{
	std::queue< Node* >		nodeQueue;

	nodeQueue.push(this);
	while (!nodeQueue.empty())
	{
		Node* currentNode = nodeQueue.front();
		
		if (currentNode->getName() == name)
			return currentNode;

		nodeQueue.pop();

		for (unsigned i = 0; i < currentNode->getChildrenCount(); ++i)
			nodeQueue.push(currentNode->getChild(i));
	}
	return NULL;
}

void Node::attachChild(Node* node)
{
	if (node == 0) throw std::invalid_argument(__FUNCTION__);

	m_children.push_back(node);
	node->m_parent = this;
}

void Node::detachChild(Node* node)
{
	NodesCollection::iterator iter = std::find(m_children.begin(), m_children.end(), node);

	if (iter != m_children.end())
	{
		m_children.erase(iter);
	}
}

void Node::attachObject(Object* object)
{
	m_object = object;
}

void Node::detachObject()
{
	m_object = NULL;
}

const Node* Node::getRoot() const
{
	const Node* root = this;

	while (root->m_parent != NULL)
	{
		root = root->m_parent;
	}

	return root;
}

void Node::lookAt(const Point3f& p)
{
	Vector3f dir = Vector3f(p.x, p.y, p.z) - Vector3f(m_transform.pos);
	Vector3f up = Vector3f(m_transform.up);

	dir.normalize();

	Vector3f right = Vector3f::cross(up, dir);
	up = Vector3f::cross(dir, right);

	right.normalize();
	up.normalize();
	dir.normalize();

	m_transform.right = Vector4f(right, 0.0f);
	m_transform.up = Vector4f(up, 0.0f);
	m_transform.at = Vector4f(dir, 0.0f);
}

void Node::update()
{
}

void Node::render()
{
	Renderer& renderer(GraphicsManager::getInstance().getRenderer());

	renderer.setColor(m_color);
	renderer.pushMatrix();

	renderer.multMatrix(getTransform());
	if (!m_hidden && m_object != NULL)
	{
		m_object->render();
	}

	renderer.setColor(Colorf::White);
	
	for (NodesCollection::const_iterator iter = m_children.begin(); iter != m_children.end(); ++iter)
		(*iter)->render();
	
	renderer.popMatrix();
}

std::string Node::getSignature() const
{
	return "dw::graphics::Node";
}

Matrix4f Node::getViewMatrix() const
{
	return dw::maths::Matrix4f::view(m_transform);
}

void Node::serialize(ISerializer* serializer) const
{
	//serializer->serialize(_name);
	//serializer->serialize(_color);
	//serializer->serialize(_position);
	//serializer->serialize(_orientation);
	//serializer->serialize(_scale);
	//serializer->serialize(_hidden);
}

void Node::deserialize(ISerializer* serializer)
{
	//serializer->deserialize(_name);
	//serializer->deserialize(_color);
	//serializer->deserialize(_position);
	//serializer->deserialize(_orientation);
	//serializer->deserialize(_scale);
	//serializer->deserialize(_hidden);
}

std::ostream& operator<<(std::ostream& os, const Node& node)
{
	unsigned nameLength	= static_cast< unsigned >(node.m_name.size());

	os.write(reinterpret_cast< const char* >(&nameLength), sizeof (unsigned));
	os.write(node.m_name.c_str(), nameLength + 1);
	os.write(reinterpret_cast< const char* >(&node.m_color), sizeof (Colorf));
	os.write(reinterpret_cast< const char* >(&node.m_transform), sizeof (Matrix4f));
	os.write(reinterpret_cast< const char* >(&node.m_hidden), sizeof (bool));

	return os;
}

std::istream& operator>>(std::istream& is, Node& node)
{
	char* name;
	unsigned nameLength;

	is.read(reinterpret_cast< char* >(&nameLength), sizeof (unsigned));
	name = new char[nameLength + 1];
	is.read(name, nameLength + 1);
	node.m_name.assign(name, nameLength);
	delete [] name;
	is.read(reinterpret_cast< char* >(&node.m_color), sizeof (Colorf));
	is.read(reinterpret_cast< char* >(&node.m_transform), sizeof (Matrix4f));
	is.read(reinterpret_cast< char* >(&node.m_hidden), sizeof (bool));
	return is;
}

	} // namespace graphics
} // namespace dw
